package com.minivision.fdiot.rest;

import java.io.IOException;
import java.util.*;

import javax.validation.Valid;

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import com.minivision.fdiot.annotation.Log;
import com.minivision.fdiot.common.RestResult;
import com.minivision.fdiot.entity.Face;
import com.minivision.fdiot.exception.ErrorType;
import com.minivision.fdiot.exception.FacePlatException;
import com.minivision.fdiot.rest.param.faceset.AddFaceParam;
import com.minivision.fdiot.rest.param.faceset.BatchAddFaceParam;
import com.minivision.fdiot.rest.param.faceset.DetectAndAddFaceParam;
import com.minivision.fdiot.rest.param.faceset.RemoveFaceParam;
import com.minivision.fdiot.rest.param.faceset.SetCreateParam;
import com.minivision.fdiot.rest.param.faceset.SetDeleteParam;
import com.minivision.fdiot.rest.param.faceset.SetDetailParam;
import com.minivision.fdiot.rest.param.faceset.SetListParam;
import com.minivision.fdiot.rest.param.faceset.SetMergeParam;
import com.minivision.fdiot.rest.param.faceset.SetModifyParam;
import com.minivision.fdiot.rest.result.detect.DetectedFace;
import com.minivision.fdiot.rest.result.faceset.AddFaceResult;
import com.minivision.fdiot.rest.result.faceset.BatchAddFaceFailResult;
import com.minivision.fdiot.rest.result.faceset.BatchAddFaceResult;
import com.minivision.fdiot.rest.result.faceset.BatchAddFaceSuccessResult;
import com.minivision.fdiot.rest.result.faceset.RemoveFaceResult;
import com.minivision.fdiot.rest.result.faceset.SetCreateResult;
import com.minivision.fdiot.rest.result.faceset.SetDeleteResult;
import com.minivision.fdiot.rest.result.faceset.SetDetailResult;
import com.minivision.fdiot.rest.result.faceset.SetListResult;
import com.minivision.fdiot.rest.result.faceset.SetMergeResult;
import com.minivision.fdiot.rest.result.faceset.SetModifyResult;
import com.minivision.fdiot.service.FaceService;
import com.minivision.fdiot.service.FaceSetService;
import com.minivision.fdiot.util.FeatureUtils;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping(value = "api/v1/faceset", method = RequestMethod.POST)
@Api(tags = "FaceSetApi", value = "FaceSet Apis")
@Slf4j
public class FaceSetApi {
	
	@Autowired
	private FaceSetService faceSetService;
	
	@Autowired
    private FaceService faceService;

	/**
	 * 创建人脸库
	 * @param param
	 * @return
	 * @throws FacePlatException
	 */
	@RequestMapping(value = "create")
	@ApiOperation(value="创建人脸库", notes="创建人脸库")
	@Log(module = "FaceSetApi", operation = "创建人脸库")
	public RestResult<SetCreateResult> create(@Valid @ModelAttribute SetCreateParam param) throws FacePlatException {
		RestResult<SetCreateResult> result = new RestResult<>();
		if(!StringUtils.isEmpty(param.getExtend())){
			Gson gson = new Gson();
			Map<String,String> map = new HashMap<>();
			map = gson.fromJson(param.getExtend(),Map.class);
			param.setExtendMap(map);
		}


		result.setData(faceSetService.create(param));
		return result;
	}

	/**
	 * 添加人脸comm
	 * @param param
	 * @return
	 * @throws FacePlatException
	 */
	@RequestMapping(value = "addface")
	@ApiOperation(value="添加人脸", notes="添加人脸")
	@Log(module = "FaceSetApi", operation = "添加人脸")
	public RestResult<AddFaceResult> addFace(@Valid @ModelAttribute AddFaceParam param) throws FacePlatException {
		RestResult<AddFaceResult> result = new RestResult<>();
		result.setData(faceSetService.addFace(param.isNeedPush2Device(), param.getFacesetToken(),param.getAppKey(), param.getFaceTokens().split(",")));
		return result;
	}
	
	@RequestMapping(value = "directAdd", consumes="multipart/form-data")
    @ApiOperation(value="人脸检测并添加人脸", notes="人脸检测并添加人脸")
    @ApiImplicitParams({
       @ApiImplicitParam(name = "imageFile", paramType = "form", dataType="file")
    })
	@Log(module = "FaceSetApi", operation = "人脸检测并添加人脸")
    public RestResult<AddFaceResult> directAdd(@Valid @ModelAttribute DetectAndAddFaceParam param) throws FacePlatException, IOException {
	    MultipartFile imageFile = param.getImageFile();
        log.info("detect and add face image:{}-{}", imageFile.getOriginalFilename(), imageFile.getSize());
	    RestResult<AddFaceResult> result = new RestResult<>();
		//modify by guming 人脸检测并添加人脸接口directAdd方法修改返回照片中人脸四个坐标点和旋转角度
	    List<DetectedFace> detectResult = faceService.detect(imageFile.getBytes(), true);
	    //end modify
	    if (CollectionUtils.isEmpty(detectResult)) {
          throw new FacePlatException(ErrorType.FACE_NOT_EXIST);
        }
	    String faceToken = detectResult.get(0).getFaceToken();
	    AddFaceResult data = faceSetService.addFace(param.isNeedPush2Device(), param.getFacesetToken(), param.getAppKey(), faceToken);
	    data.setFaceToken(faceToken);
	    //是否需要人脸坐标点
	    if (param.isNeedFaceRectangle()) {
			data.setRectangle(detectResult.get(0).getFaceRectangle());
		}
	    result.setData(data);
        return result;
    }
	
	  /**
	   * 批量导入人脸
	   * @param param
	   * @return
	   * @throws FacePlatException
	   */
	  @RequestMapping(value = "batchAdd", consumes = "multipart/form-data")
	  @ApiOperation(value="批量导入人脸", notes="批量导入人脸")
	  @ApiImplicitParams({
	    @ApiImplicitParam(name = "imageDatas", paramType = "form", dataType="file")
	  })
	  @Log(module = "FaceSetApi", operation = "批量导入人脸")
	  public RestResult<BatchAddFaceResult> batchAdd(@Valid @ModelAttribute BatchAddFaceParam param) throws FacePlatException {
	    List<BatchAddFaceSuccessResult> successResults = new ArrayList<>();
	    List<BatchAddFaceFailResult> failResults = new ArrayList<>();
	    MultipartFile[] files = param.getImageDatas();
	    Arrays.stream(files).forEach((file) -> {
	      try {
	        DetectedFace detectedFace = faceService.detectOneFace(file.getBytes());
	        Face face = faceService.saveFace2Cache(file, detectedFace);
	        faceSetService.addFace(param.isNeedPush2Device(), param.getFacesetToken(), param.getAppKey(), face.getToken());
	        BatchAddFaceSuccessResult successResult = new BatchAddFaceSuccessResult();
	        successResult.setImgName(file.getOriginalFilename());
	        if (param.isGetFeature()) {
	          successResult.setFeature(FeatureUtils.encode(face.getFeature()));
            }
	        successResult.setFaceToken(face.getToken());
	        successResults.add(successResult);
	      } catch (Exception e) {
	        log.error("add face failed, image:" + file.getOriginalFilename(), e);
	        BatchAddFaceFailResult failResult = new BatchAddFaceFailResult();
	        failResult.setImgName(file.getOriginalFilename());
	        failResult.setReason(e.getMessage());
	        failResults.add(failResult);
	      }
	    });

	    BatchAddFaceResult result = new BatchAddFaceResult();
	    result.setSuccessResults(successResults);
	    result.setFailResults(failResults);
	    return new RestResult<BatchAddFaceResult>(result);
	  }
	
	/**
	 * 删除人脸库
	 * @param param
	 * @return
	 * @throws FacePlatException 
	 */
	@RequestMapping(value = "delete")
	@ApiOperation(value="删除人脸库", notes="删除人脸库")
	@Log(module = "FaceSetApi", operation = "删除人脸库")
	public RestResult<SetDeleteResult> delete(@Valid @ModelAttribute SetDeleteParam param) throws FacePlatException {
		RestResult<SetDeleteResult> result = new RestResult<>();
		result.setData(faceSetService.delete(param.getFacesetToken(), param.isForce(), param.getAppKey(),param.isNeedPush2Device()));
		return result;
	}

	/**
	 * 删除人脸
	 * @param param
	 * @return
	 * @throws FacePlatException
	 */
	@RequestMapping(value = "removeface")
	@ApiOperation(value="删除人脸", notes="删除人脸")
	@Log(module = "FaceSetApi", operation = "删除人脸")
	public RestResult<RemoveFaceResult> removeFace(@Valid @ModelAttribute RemoveFaceParam param) throws FacePlatException {
		RestResult<RemoveFaceResult> result = new RestResult<>();
		result.setData(faceSetService.removeFace(param.isNeedPush2Device(), param.getFacesetToken(),param.getAppKey(),param.getFaceTokens().split(",")));
		return result;
	}
	
	/**
	 * 修改人脸库
	 * @param param
	 * @return
	 * @throws FacePlatException 
	 */
	@RequestMapping(value = "modify")
	@ApiOperation(value="修改人脸库", notes="修改人脸库")
	@Log(module = "FaceSetApi", operation = "修改人脸库")
	public RestResult<SetModifyResult> modify(@Valid @ModelAttribute SetModifyParam param) throws FacePlatException {
		RestResult<SetModifyResult> result = new RestResult<>();
		result.setData(faceSetService.modify(param));
		return result;
	}
	
	/**
     * 查看人脸库列表
     * @param param
     * @return
     * @throws FacePlatException 
     */
    @RequestMapping(value = "list")
    @ApiOperation(value="查询人脸库列表", notes="查询人脸库列表")
	public RestResult<SetListResult> getFaceSetList(@Valid @ModelAttribute SetListParam param) throws FacePlatException {
	  RestResult<SetListResult> result = new RestResult<>();
	  SetListResult data = faceSetService.getFaceSetList(param.getOffset(), param.getLimit(),param.getAppKey());
	  result.setData(data);
	  return result;
	}
	
	/**
	 * 查看人脸库信息
	 * @param param
	 * @return
	 * @throws FacePlatException 
	 */
	@RequestMapping(value = "getdetail")
	@ApiOperation(value="查看人脸库信息", notes="查看人脸库信息")
	public RestResult<SetDetailResult> getdetail(@Valid @ModelAttribute SetDetailParam param) throws FacePlatException {
		RestResult<SetDetailResult> result = new RestResult<>();
		result.setData(faceSetService.getFaceSetDetail(param.getFacesetToken(), param.getFaceOffset(), param.getFaceCount(),param.getAppKey()));
		return result;
	}

	/**
	 * 合并人脸库
	 * @param param
	 * @return
	 * @throws FacePlatException 
	 */
	@RequestMapping(value = "merge")
	@ApiOperation(value="合并人脸库", notes="合并人脸库")
	@Log(module = "FaceSetApi", operation = "合并人脸库")
	public RestResult<SetMergeResult> merge(@Valid @ModelAttribute SetMergeParam param) throws FacePlatException {
		RestResult<SetMergeResult> result = new RestResult<>();
		result.setData(faceSetService.mergeSet(param.getAppKey(), param.getFacesetToken1(), param.getFacesetToken2(),param.isNeedPush2Device()));
		return result;
	}

}
